' Heightmap demo 5
'
' Using a display list to speed things up
'
const heightScale# = 10 ' Height map scale factor
const texScale# = 0.1   ' Number of textures per grid square
dim xSize, ySize
dim xMask, yMask        ' bitmasks to apply to X and Y coordinates
dim file                ' File handle
dim x, y                ' Working variables
dim angle#              ' Viewing angle (used for animation)
dim texture             ' OpenGL texture handle for our texture
dim tx#, ty#            ' Texture coordinates
dim a#(2), b#(2), c#(2), d#(2), norm#(2) ' Temporary vectors used for lighting
dim intensity#
dim displayList         ' Our display list handle

goto Start

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Routines
CheckError:
    ' Check for file error
    if FileError () <> "" then
        print FileError ()
        CloseFile (file)
        end
    endif
    return

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Main program stars here    
Start:

' Open file
file = OpenFileRead ("Files\Hills01.dat")
gosub CheckError

' Read X and Y size
xSize = Val (ReadLine (file))
ySize = Val (ReadLine (file))
gosub CheckError

' Now that size is known, we can dim our 2D array
dim h#(xSize - 1)(ySize - 1)        
xMask = xSize - 1
yMask = ySize - 1

for y = 0 to ySize - 1
    for x = 0 to xSize - 1
        h#(x)(y) = val (ReadText (file, true)) * heightScale#
        gosub CheckError
    next
next          
CloseFile (file)

' Create light map
dim lightSource#(2)
lightSource# = vec3(0, -1, 0)           ' Light shines straight down.
lightSource# = Normalize (lightSource#) ' Light source must be length 1

' Create light map
dim l# (xSize - 1)(ySize - 1)
for x = 0 to xSize - 1
    for y = 0 to ySize - 1
        a# = vec3 (x,   h#(x)(y),                             y)
        b# = vec3 (x+1, h#((x+1) and xMask)(y),               y)
        c# = vec3 (x  , h#(x              )((y+1) and yMask), y+1)        
        d# = vec3 (x+1, h#((x+1) and xMask)((y+1) and yMask), y+1)

        norm# = Normalize (CrossProduct (d# - a#, b# - c#))        

        intensity# = norm# * -lightSource#

        intensity# = intensity# * intensity# * intensity#
        if intensity# < 0.2 then intensity# = 0.2 endif
            
        l#(x)(y) = intensity#
    next
next

' Load texture file 
texture = LoadMipmapTexture ("textures\00005.jpg")

' Enable texture mapping, and bind our texture
glEnable (GL_TEXTURE_2D)
glBindTexture (GL_TEXTURE_2D, texture)

' Create display list
' A dispay list is a list of OpenGL commands that we will be executing often.
' Instead of executing ALL the OpenGL commands one-by-one each time, we can
' place them all into a display list, and then tell OpenGL to run the display
' list.

' This will usually speed things up, especially in situations like this one
' where our humble virtual machine struggles to output several thousand 
' vertex, colour and texture commands each frame.

' Create our display list handle
displayList = glGenLists (1)

' Compile the display list.
' Instead of performing the drawing commands, OpenGL will save them into the
' display list.
glNewList (displayList, GL_COMPILE)

    ' Draw the heightmap points
    for x = 0 to xSize - 2
        for y = 0 to ySize - 2
        
            ' Calculate the texture coordinates
            tx# = x * texScale#
            ty# = y * texScale#

            ' Draw a grid square.
            ' Apply the lightmap, by setting the colour at each corner.
            ' We will multiply the colour white (1,1,1) as a vector by the light
            ' intensity. Then use the resulting vector as our colour coordinates
            glBegin (GL_QUADS)
                intensity# = l#(x)(y)
                glColor3f (intensity#, intensity#, intensity#)
                glTexCoord2f (tx#            , ty#)
                glVertex3f (x  , h#(x  )(y  ), y  )
                
                intensity# = l#(x+1)(y)
                glColor3f (intensity#, intensity#, intensity#)
                glTexCoord2f (tx# + texScale#, ty#)
                glVertex3f (x+1, h#(x+1)(y  ), y  )

                intensity# = l#(x+1)(y+1)
                glColor3f (intensity#, intensity#, intensity#)
                glTexCoord2f (tx# + texScale#, ty# + texScale#)
                glVertex3f (x+1, h#(x+1)(y+1), y+1)

                intensity# = l#(x)(y+1)
                glColor3f (intensity#, intensity#, intensity#)
                glTexCoord2f (tx#            , ty# + texScale#)
                glVertex3f (x  , h#(x  )(y+1), y+1)
            glEnd ()
        next
    next
glEndList ()
' Now everytime we want to draw the entire grid, we can call glCallList (displayList)

while true
    
    ' Clear the screen
    glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
        
    ' Setup the transformations
    glLoadIdentity ()                       ' Clear out any existing transformations
    glTranslatef (0, 0, -50)                ' "push" map away
    glRotatef (20, 1, 0, 0)                 ' Elevation rotation
    glRotatef (angle#, 0, 1, 0)             ' Animation rotation
    glTranslatef (-xSize/2, 0, -ySize/2)    ' Centre the map    

    ' Draw the heightmap points, using our display list
    glCallList (displayList)

    ' Show the result
    SwapBuffers ()
    
    ' Animation
    while SyncTimer (10)
        angle# = angle# + 0.4
    wend
wend